<!DOCTYPE html>
<html lang="en">
  <head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
  <meta content="" name="keywords">

  <title> Docker - </title>
  <meta name="description" content="" />
  <meta property="og:title" content="Docker" />
  <meta name="twitter:title" content="Docker" />
  <meta name="author" content="{Description { .Site.Author.name }}"/>
  <meta name="twitter:card" content="summary" />
  <meta property="og:url" content="https://systemime.github.io/categories/docker/" />
  <meta property="og:type" content="website" />
  <meta property="og:site_name" content="听风的小站" />

  <meta name="generator" content="Hugo 0.67.0" />
  <link rel="canonical" href="https://systemime.github.io/categories/docker/" />
  <link rel="alternate" href="https://systemime.github.io/index.xml" type="application/rss+xml" title="听风的小站">
  <script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/jquery-1.12.4.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css" integrity="sha384-wITovz90syo1dJWVh32uuETPVEtGigN07tkttEqPv+uR2SE/mbQcG7ATL28aI9H0" crossorigin="anonymous">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  
  
  <link rel="stylesheet" href="https://systemime.github.io/css/main.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/my.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/pace.css" />
  <link rel="stylesheet" href="https://cdn.plyr.io/3.4.7/plyr.css">
  <link rel="stylesheet" href="https://systemime.github.io/css/lightgallery.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/baguetteBox.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/search.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/reward.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/share.min.css" />
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic" />
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" />
  <link rel="stylesheet" href="https://systemime.github.io/css/pygment_highlights.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/highlight.min.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/toc.css" />
  <link rel="stylesheet" href="https://systemime.github.io/css/lightbox.css" />
  <link rel="stylesheet" href="https://systemime.github.io/live2d/css/live2d.css" />
  
  <link href="https://systemime.github.io/css/video-js.css" rel="stylesheet">


<meta name="baidu-site-verification" content="g8IYR9SNLF" />
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?cffe439e37449bb1c07ab26ab56484bb";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

<link rel="stylesheet" href="https://systemime.github.io/css/prism.css" />









<script src="https://systemime.github.io/js/pace.min.js" data-no-instant></script>



<script src="https://systemime.github.io/js/instantclick.min.js" data-no-instant></script>
   <script data-no-instant>
   InstantClick.on('change', function(isInitialLoad) {
     if (isInitialLoad === false) {
       if (typeof MathJax !== 'undefined') 
         MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
       if (typeof prettyPrint !== 'undefined') 
         prettyPrint();
       if (typeof _hmt !== 'undefined')  
         _hmt.push(['_trackPageview', location.pathname + location.search]);
       if (typeof ga !== 'undefined')  
           ga('send', 'pageview', location.pathname + location.search);
     }
   });
   InstantClick.init();
</script>




<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.7.1/gist-embed.min.js"></script>


<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
  (adsbygoogle = window.adsbygoogle || []).push({
    google_ad_client: "ca-pub-3925981084585036",
    enable_page_level_ads: true
  });
</script>



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/photoswipe.min.css" integrity="sha256-sCl5PUOGMLfFYctzDW3MtRib0ctyUvI9Qsmq2wXOeBY=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.1/default-skin/default-skin.min.css" integrity="sha256-BFeI1V+Vh1Rk37wswuOYn5lsTcaU96hGaI7OUVCLjPc=" crossorigin="anonymous" />



<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

<div class="pswp__bg"></div>

<div class="pswp__scroll-wrap">
    
    <div class="pswp__container">
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
      <div class="pswp__item"></div>
    </div>
    
    <div class="pswp__ui pswp__ui--hidden">
    <div class="pswp__top-bar">
      
      <div class="pswp__counter"></div>
      <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
      <button class="pswp__button pswp__button--share" title="Share"></button>
      <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
      <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
      
      
      <div class="pswp__preloader">
        <div class="pswp__preloader__icn">
          <div class="pswp__preloader__cut">
            <div class="pswp__preloader__donut"></div>
          </div>
        </div>
      </div>
    </div>
    <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
      <div class="pswp__share-tooltip"></div>
    </div>
    <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
    </button>
    <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
    </button>
    <div class="pswp__caption">
      <div class="pswp__caption__center"></div>
    </div>
    </div>
    </div>
</div>

</head>

  <body>
    <nav class="navbar navbar-default navbar-fixed-top navbar-custom">
  <div class="container-fluid">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#main-navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="https://systemime.github.io">听风的小站</a>
    </div>
    <div class="collapse navbar-collapse" id="main-navbar">
      <ul class="nav navbar-nav navbar-right">
        
          
            <li>
		    <a title="博客" href="https://systemime.github.io/blog/">博客</a>
              
              
            </li>
          
        
          
            <li>
		    <a title="片刻" href="https://systemime.github.io/moment/">片刻</a>
              
              
            </li>
          
        
          
            <li>
		    <a title="关于" href="https://systemime.github.io/about/">关于</a>
              
              
            </li>
          
        
          
            <li>
		    <a title="留言" href="https://systemime.github.io/message/">留言</a>
              
              
            </li>
          
        

        

        
        
      </ul>
    </div>

    <div class="avatar-container">
      <div class="avatar-img-border">
        
      </div>
    </div>

  </div>
</nav>






    
  <header class="header-section ">
    <div class="intro-header no-img">
      <div class="container">
        <div class="row">
          <div class="col-lg-12 col-md-12 col-md-offset-0">
            <div class="page-heading">
              <h1>#Docker</h1>
              <hr class="small">
            </div>
          </div>
        </div>
      </div>
    </div>
  </header>

    
  <div class="container" role="main">
    <div class="row">
      <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
        
        <div class="posts-list">
          
            <article class="post-preview">
              <a href="https://systemime.github.io/blog/2019-02/%E4%BD%BF%E7%94%A8-dockerfile-%E5%AE%9A%E5%88%B6%E9%95%9C%E5%83%8F/">
                <h2 class="post-title">使用 Dockerfile 定制镜像</h2>

                
              </a>

              <p class="post-meta">
                Posted on February 14, 2019
              </p>
              <div class="post-entry">
                
                  <h2 id="使用-dockerfile-定制镜像">使用 Dockerfile 定制镜像</h2>
<blockquote>
<p>原文地址：<a href="https://yeasy.gitbooks.io/docker_practice/image/build.html#">https://yeasy.gitbooks.io/docker_practice/image/build.html#</a>
项目地址：<a href="https://github.com/yeasy/docker_practice">https://github.com/yeasy/docker_practice</a></p>
</blockquote>
<p>从刚才的 <code>docker commit</code> 的学习中，我们可以了解到，镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本，用这个脚本来构建、定制镜像，那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。</p>
<p>Dockerfile 是一个文本文件，其内包含了一条条的<strong>指令(Instruction)</strong>，每一条指令构建一层，因此每一条指令的内容，就是描述该层应当如何构建。</p>
<p>还以之前定制 <code>nginx</code> 镜像为例，这次我们使用 Dockerfile 来定制。</p>
<p>在一个空白目录中，建立一个文本文件，并命名为 <code>Dockerfile</code>：</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ mkdir mynginx
$ <span class="nb">cd</span> mynginx
$ touch Dockerfile
</code></pre></div><p>其内容为：</p>
<div class="highlight"><pre class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> nginx</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> <span class="nb">echo</span> <span class="s1">&#39;&lt;h1&gt;Hello, Docker!&lt;/h1&gt;&#39;</span> &gt; /usr/share/nginx/html/index.html<span class="err">
</span></code></pre></div><p>这个 Dockerfile 很简单，一共就两行。涉及到了两条指令，<code>FROM</code> 和 <code>RUN</code>。</p>
<h3 id="from-指定基础镜像">FROM 指定基础镜像</h3>
<p>所谓定制镜像，那一定是以一个镜像为基础，在其上进行定制。就像我们之前运行了一个 <code>nginx</code> 镜像的容器，再进行修改一样，基础镜像是必须指定的。而 <code>FROM</code> 就是指定<strong>基础镜像</strong>，因此一个 <code>Dockerfile</code> 中 <code>FROM</code> 是必备的指令，并且必须是第一条指令。</p>
<p>在 <a href="https://hub.docker.com/search?q=&amp;type=image&amp;image_filter=official">Docker Hub</a> 上有非常多的高质量的官方镜像，有可以直接拿来使用的服务类的镜像，如 <a href="https://hub.docker.com/_/nginx/"><code>nginx</code></a>、<a href="https://hub.docker.com/_/redis/"><code>redis</code></a>、<a href="https://hub.docker.com/_/mongo/"><code>mongo</code></a>、<a href="https://hub.docker.com/_/mysql/"><code>mysql</code></a>、<a href="https://hub.docker.com/_/httpd/"><code>httpd</code></a>、<a href="https://hub.docker.com/_/php/"><code>php</code></a>、<a href="https://hub.docker.com/_/tomcat/"><code>tomcat</code></a> 等；也有一些方便开发、构建、运行各种语言应用的镜像，如 <a href="https://hub.docker.com/_/node"><code>node</code></a>、<a href="https://hub.docker.com/_/openjdk/"><code>openjdk</code></a>、<a href="https://hub.docker.com/_/python/"><code>python</code></a>、<a href="https://hub.docker.com/_/ruby/"><code>ruby</code></a>、<a href="https://hub.docker.com/_/golang/"><code>golang</code></a> 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。</p>
<p>如果没有找到对应服务的镜像，官方镜像中还提供了一些更为基础的操作系统镜像，如 <a href="https://hub.docker.com/_/ubuntu/"><code>ubuntu</code></a>、<a href="https://hub.docker.com/_/debian/"><code>debian</code></a>、<a href="https://hub.docker.com/_/centos/"><code>centos</code></a>、<a href="https://hub.docker.com/_/fedora/"><code>fedora</code></a>、<a href="https://hub.docker.com/_/alpine/"><code>alpine</code></a> 等，这些操作系统的软件库为我们提供了更广阔的扩展空间。</p>
<p>除了选择现有镜像为基础镜像外，Docker 还存在一个特殊的镜像，名为 <code>scratch</code>。这个镜像是虚拟的概念，并不实际存在，它表示一个空白的镜像。</p>
<div class="highlight"><pre class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> scratch</span><span class="err">
</span><span class="err"></span>...<span class="err">
</span></code></pre></div><p>如果你以 <code>scratch</code> 为基础镜像的话，意味着你不以任何镜像为基础，接下来所写的指令将作为镜像第一层开始存在。</p>
<p>不以任何系统为基础，直接将可执行文件复制进镜像的做法并不罕见，比如 <a href="https://hub.docker.com/_/swarm/"><code>swarm</code></a>、<a href="https://quay.io/repository/coreos/etcd"><code>coreos/etcd</code></a>。对于 Linux 下静态编译的程序来说，并不需要有操作系统提供运行时支持，所需的一切库都已经在可执行文件里了，因此直接 <code>FROM scratch</code> 会让镜像体积更加小巧。使用 <a href="https://golang.org/">Go 语言</a> 开发的应用很多会使用这种方式来制作镜像，这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。</p>
<h3 id="run-执行命令">RUN 执行命令</h3>
<p><code>RUN</code> 指令是用来执行命令行命令的。由于命令行的强大能力，<code>RUN</code> 指令在定制镜像时是最常用的指令之一。其格式有两种：</p>
<ul>
<li><em>shell</em> 格式：<code>RUN &lt;命令&gt;</code>，就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 <code>RUN</code> 指令就是这种格式。</li>
</ul>
<div class="highlight"><pre class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="k">RUN</span> <span class="nb">echo</span> <span class="s1">&#39;&lt;h1&gt;Hello, Docker!&lt;/h1&gt;&#39;</span> &gt; /usr/share/nginx/html/index.html<span class="err">
</span></code></pre></div><ul>
<li><em>exec</em> 格式：<code>RUN [&quot;可执行文件&quot;, &quot;参数1&quot;, &quot;参数2&quot;]</code>，这更像是函数调用中的格式。</li>
</ul>
<p>既然 <code>RUN</code> 就像 Shell 脚本一样可以执行命令，那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢？比如这样：</p>
<div class="highlight"><pre class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> debian:stretch</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> apt-get update<span class="err">
</span><span class="err"></span><span class="k">RUN</span> apt-get install -y gcc libc6-dev make wget<span class="err">
</span><span class="err"></span><span class="k">RUN</span> wget -O redis.tar.gz <span class="s2">&#34;http://download.redis.io/releases/redis-5.0.3.tar.gz&#34;</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> mkdir -p /usr/src/redis<span class="err">
</span><span class="err"></span><span class="k">RUN</span> tar -xzf redis.tar.gz -C /usr/src/redis --strip-components<span class="o">=</span><span class="m">1</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> make -C /usr/src/redis<span class="err">
</span><span class="err"></span><span class="k">RUN</span> make -C /usr/src/redis install<span class="err">
</span></code></pre></div><p>之前说过，Dockerfile 中每一个指令都会建立一层，<code>RUN</code> 也不例外。每一个 <code>RUN</code> 的行为，就和刚才我们手工建立镜像的过程一样：新建立一层，在其上执行这些命令，执行结束后，<code>commit</code> 这一层的修改，构成新的镜像。</p>
<p>而上面的这种写法，创建了 7 层镜像。这是完全没有意义的，而且很多运行时不需要的东西，都被装进了镜像里，比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像，不仅仅增加了构建部署的时间，也很容易出错。
这是很多初学 Docker 的人常犯的一个错误。</p>
<p><em>Union FS 是有最大层数限制的，比如 AUFS，曾经是最大不得超过 42 层，现在是不得超过 127 层。</em></p>
<p>上面的 <code>Dockerfile</code> 正确的写法应该是这样：</p>
<div class="highlight"><pre class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="k">FROM</span><span class="s"> debian:stretch</span><span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> <span class="nv">buildDeps</span><span class="o">=</span><span class="s1">&#39;gcc libc6-dev make wget&#39;</span> <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> apt-get update <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> apt-get install -y <span class="nv">$buildDeps</span> <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> wget -O redis.tar.gz <span class="s2">&#34;http://download.redis.io/releases/redis-5.0.3.tar.gz&#34;</span> <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> mkdir -p /usr/src/redis <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> tar -xzf redis.tar.gz -C /usr/src/redis --strip-components<span class="o">=</span><span class="m">1</span> <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> make -C /usr/src/redis <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> make -C /usr/src/redis install <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> rm -rf /var/lib/apt/lists/* <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> rm redis.tar.gz <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> rm -r /usr/src/redis <span class="se">\
</span><span class="se"></span>    <span class="o">&amp;&amp;</span> apt-get purge -y --auto-remove <span class="nv">$buildDeps</span><span class="err">
</span></code></pre></div><p>首先，之前所有的命令只有一个目的，就是编译、安装 redis 可执行文件。因此没有必要建立很多层，这只是一层的事情。因此，这里没有使用很多个 <code>RUN</code> 对一一对应不同的命令，而是仅仅使用一个 <code>RUN</code> 指令，并使用 <code>&amp;&amp;</code> 将各个所需命令串联起来。将之前的 7 层，简化为了 1 层。在撰写 Dockerfile 的时候，要经常提醒自己，这并不是在写 Shell 脚本，而是在定义每一层该如何构建。</p>
<p>并且，这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 <code>\</code> 的命令换行方式，以及行首 <code>#</code> 进行注释的格式。良好的格式，比如换行、缩进、注释等，会让维护、排障更为容易，这是一个比较好的习惯。</p>
<p>此外，还可以看到这一组命令的最后添加了清理工作的命令，删除了为了编译构建所需要的软件，清理了所有下载、展开的文件，并且还清理了 <code>apt</code> 缓存文件。这是很重要的一步，我们之前说过，镜像是多层存储，每一层的东西并不会在下一层被删除，会一直跟随着镜像。因此镜像构建时，一定要确保每一层只添加真正需要添加的东西，任何无关的东西都应该清理掉。</p>
<p>很多人初学 Docker 制作出了很臃肿的镜像的原因之一，就是忘记了每一层构建的最后一定要清理掉无关文件。</p>
<h3 id="构建镜像">构建镜像</h3>
<p>好了，让我们再回到之前定制的 nginx 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容，那么让我们来构建这个镜像吧。</p>
<p>在 <code>Dockerfile</code> 文件所在目录执行：</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step <span class="m">1</span> : FROM nginx
 ---&gt; e43d811ce2f4
Step <span class="m">2</span> : RUN <span class="nb">echo</span> <span class="s1">&#39;&lt;h1&gt;Hello, Docker!&lt;/h1&gt;&#39;</span> &gt; /usr/share/nginx/html/index.html
 ---&gt; Running in 9cdc27646c7b
 ---&gt; 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c
</code></pre></div><p>从命令的输出结果中，我们可以清晰的看到镜像的构建过程。在 <code>Step 2</code> 中，如同我们之前所说的那样，<code>RUN</code> 指令启动了一个容器 <code>9cdc27646c7b</code>，执行了所要求的命令，并最后提交了这一层 <code>44aa4490ce2c</code>，随后删除了所用到的这个容器 <code>9cdc27646c7b</code>。</p>
<p>这里我们使用了 <code>docker build</code> 命令进行镜像构建。其格式为：</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">docker build <span class="o">[</span>选项<span class="o">]</span> &lt;上下文路径/URL/-&gt;
</code></pre></div><p>在这里我们指定了最终镜像的名称 <code>-t nginx:v3</code>，构建成功后，我们可以像之前运行 <code>nginx:v2</code> 那样来运行这个镜像，其结果会和 <code>nginx:v2</code> 一样。</p>
<h3 id="镜像构建上下文context">镜像构建上下文（Context）</h3>
<p>如果注意，会看到 <code>docker build</code> 命令最后有一个 <code>.</code>。<code>.</code> 表示当前目录，而 <code>Dockerfile</code> 就在当前目录，因此不少初学者以为这个路径是在指定 <code>Dockerfile</code> 所在路径，这么理解其实是不准确的。如果对应上面的命令格式，你可能会发现，这是在指定<strong>上下文路径</strong>。那么什么是上下文呢？</p>
<p>首先我们要理解 <code>docker build</code> 的工作原理。Docker 在运行时分为 Docker 引擎（也就是服务端守护进程）和客户端工具。Docker 的引擎提供了一组 REST API，被称为 <a href="https://docs.docker.com/develop/sdk/">Docker Remote API</a>，而如 <code>docker</code> 命令这样的客户端工具，则是通过这组 API 与 Docker 引擎交互，从而完成各种功能。因此，虽然表面上我们好像是在本机执行各种 <code>docker</code> 功能，但实际上，一切都是使用的远程调用形式在服务端（Docker 引擎）完成。也因为这种 C/S 设计，让我们操作远程服务器的 Docker 引擎变得轻而易举。</p>
<p>当我们进行镜像构建的时候，并非所有定制都会通过 <code>RUN</code> 指令完成，经常会需要将一些本地文件复制进镜像，比如通过 <code>COPY</code> 指令、<code>ADD</code> 指令等。而 <code>docker build</code> 命令构建镜像，其实并非在本地构建，而是在服务端，也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中，如何才能让服务端获得本地文件呢？</p>
<p>这就引入了上下文的概念。当构建的时候，用户会指定构建镜像上下文的路径，<code>docker build</code> 命令得知这个路径后，会将路径下的所有内容打包，然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后，展开就会获得构建镜像所需的一切文件。</p>
<p>如果在 <code>Dockerfile</code> 中这么写：</p>
<div class="highlight"><pre class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="k">COPY</span> ./package.json /app/<span class="err">
</span></code></pre></div><p>这并不是要复制执行 <code>docker build</code> 命令所在的目录下的 <code>package.json</code>，也不是复制 <code>Dockerfile</code> 所在目录下的 <code>package.json</code>，而是复制 <strong>上下文（context）</strong> 目录下的 <code>package.json</code>。</p>
<p>因此，<code>COPY</code> 这类指令中的源文件的路径都是<em>相对路径</em>。这也是初学者经常会问的为什么 <code>COPY ../package.json /app</code> 或者 <code>COPY /opt/xxxx /app</code> 无法工作的原因，因为这些路径已经超出了上下文的范围，Docker 引擎无法获得这些位置的文件。如果真的需要那些文件，应该将它们复制到上下文目录中去。</p>
<p>现在就可以理解刚才的命令 <code>docker build -t nginx:v3 .</code> 中的这个 <code>.</code>，实际上是在指定上下文的目录，<code>docker build</code> 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。</p>
<p>如果观察 <code>docker build</code> 输出，我们其实已经看到了这个发送上下文的过程：</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...
</code></pre></div><p>理解构建上下文对于镜像构建是很重要的，避免犯一些不应该的错误。比如有些初学者在发现 <code>COPY /opt/xxxx /app</code> 不工作后，于是干脆将 <code>Dockerfile</code> 放到了硬盘根目录去构建，结果发现 <code>docker build</code> 执行后，在发送一个几十 GB 的东西，极为缓慢而且很容易构建失败。那是因为这种做法是在让 <code>docker build</code> 打包整个硬盘，这显然是使用错误。</p>
<p>一般来说，应该会将 <code>Dockerfile</code> 置于一个空目录下，或者项目根目录下。如果该目录下没有所需文件，那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎，那么可以用 <code>.gitignore</code> 一样的语法写一个 <code>.dockerignore</code>，该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。</p>
<p>那么为什么会有人误以为 <code>.</code> 是指定 <code>Dockerfile</code> 所在目录呢？这是因为在默认情况下，如果不额外指定 <code>Dockerfile</code> 的话，会将上下文目录下的名为 <code>Dockerfile</code> 的文件作为 Dockerfile。</p>
<p>这只是默认行为，实际上 <code>Dockerfile</code> 的文件名并不要求必须为 <code>Dockerfile</code>，而且并不要求必须位于上下文目录中，比如可以用 <code>-f ../Dockerfile.php</code> 参数指定某个文件作为 <code>Dockerfile</code>。</p>
<p>当然，一般大家习惯性的会使用默认的文件名 <code>Dockerfile</code>，以及会将其置于镜像构建上下文目录中。</p>
<h3 id="其它-docker-build-的用法">其它 <code>docker build</code> 的用法</h3>
<h4 id="直接用-git-repo-进行构建">直接用 Git repo 进行构建</h4>
<p>或许你已经注意到了，<code>docker build</code> 还支持从 URL 构建，比如可以直接从 Git repo 中构建：</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:11.1

Sending build context to Docker daemon 2.048 kB
Step <span class="m">1</span> : FROM gitlab/gitlab-ce:11.1.0-ce.0
11.1.0-ce.0: Pulling from gitlab/gitlab-ce
aed15891ba52: Already exists
773ae8583d14: Already exists
...
</code></pre></div><p>这行命令指定了构建所需的 Git repo，并且指定默认的 <code>master</code> 分支，构建目录为 <code>/11.1/</code>，然后 Docker 就会自己去 <code>git clone</code> 这个项目、切换到指定分支、并进入到指定目录后开始构建。</p>
<h4 id="用给定的-tar-压缩包构建">用给定的 tar 压缩包构建</h4>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ docker build http://server/context.tar.gz
</code></pre></div><p>如果所给出的 URL 不是个 Git repo，而是个 <code>tar</code> 压缩包，那么 Docker 引擎会下载这个包，并自动解压缩，以其作为上下文，开始构建。</p>
<h4 id="从标准输入中读取-dockerfile-进行构建">从标准输入中读取 Dockerfile 进行构建</h4>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">docker build - &lt; Dockerfile
</code></pre></div><p>或</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">cat Dockerfile <span class="p">|</span> docker build -
</code></pre></div><p>如果标准输入传入的是文本文件，则将其视为 <code>Dockerfile</code>，并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容，它没有上下文，因此不可以像其他方法那样可以将本地文件 <code>COPY</code> 进镜像之类的事情。</p>
<h4 id="从标准输入中读取上下文压缩包进行构建">从标准输入中读取上下文压缩包进行构建</h4>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ docker build - &lt; context.tar.gz
</code></pre></div><p>如果发现标准输入的文件格式是 <code>gzip</code>、<code>bzip2</code> 以及 <code>xz</code> 的话，将会使其为上下文压缩包，直接将其展开，将里面视为上下文，并开始构建。</p>

                
              </div>

              
                <span class="post-meta">
                  
                    #<a href="https://systemime.github.iotags/docker/">Docker</a>&nbsp;
                  
                    #<a href="https://systemime.github.iotags/dockerfile/">Dockerfile</a>&nbsp;
                  
                </span>
              

            </article>
          
            <article class="post-preview">
              <a href="https://systemime.github.io/blog/2019-02/ubuntu-%E5%AE%89%E8%A3%85-docker-ce/">
                <h2 class="post-title">Ubuntu 安装 Docker CE</h2>

                
              </a>

              <p class="post-meta">
                Posted on February 12, 2019
              </p>
              <div class="post-entry">
                
                  <h1 id="ubuntu-安装-docker-ce">Ubuntu 安装 Docker CE</h1>
<blockquote>
<p>警告：切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker.</p>
</blockquote>
<h3 id="系统要求">系统要求</h3>
<p>Docker CE 支持以下版本的 Ubuntu 操作系统：</p>
<ul>
<li>Bionic 18.04 (LTS)</li>
<li>Xenial 16.04 (LTS)</li>
<li>Trusty 14.04 (LTS) (Docker CE v18.06 及以下版本)</li>
</ul>
<p>Docker CE 可以安装在 64 位的 x86 平台或 ARM 平台上。Ubuntu 发行版中，LTS（Long-Term-Support）长期支持版本，会获得 5 年的升级维护支持，这样的版本会更稳定，因此在生产环境中推荐使用 LTS 版本。</p>
<h3 id="卸载旧版本">卸载旧版本</h3>
<p>旧版本的 Docker 称为<strong><code>docker</code></strong>或者<strong><code>docker-engine</code></strong>，使用以下命令卸载旧版本：</p>
<pre><code>$ sudo apt-get remove docker \
               docker-engine \
               docker.io
</code></pre><h3 id="ubuntu-1404-可选内核模块">Ubuntu 14.04 可选内核模块</h3>
<p>从 Ubuntu 14.04 开始，一部分内核模块移到了可选内核模块包 (<strong><code>linux-image-extra-*</code></strong>) ，以减少内核软件包的体积。正常安装的系统应该会包含可选内核模块包，而一些裁剪后的系统可能会将其精简掉。<strong><code>AUFS</code></strong>内核驱动属于可选内核模块的一部分，作为推荐的 Docker 存储层驱动，一般建议安装可选内核模块包以使用<strong><code>AUFS</code></strong>。
如果系统没有安装可选内核模块的话，可以执行下面的命令来安装可选内核模块包：</p>
<pre><code>$ sudo apt-get update

$ sudo apt-get install \
    linux-image-extra-$(uname -r) \
    linux-image-extra-virtual
</code></pre><h2 id="ubuntu-1604-">Ubuntu 16.04 +</h2>
<p>Ubuntu 16.04 + 上的 Docker CE 默认使用 overlay2 存储层驱动,无需手动配置。</p>
<h3 id="使用-apt-安装">使用 APT 安装</h3>
<p>由于<strong><code>apt</code></strong>源使用 HTTPS 以确保软件下载过程中不被篡改。因此，我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。</p>
<pre><code>$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
</code></pre><p>鉴于国内网络问题，强烈建议使用国内源，官方源请在注释中查看。</p>
<p>为了确认所下载软件包的合法性，需要添加软件源的<strong><code>GPG</code></strong>密钥。</p>
<pre><code>$ curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -


# 官方源
# $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
</code></pre><p>然后，我们需要向<strong><code>source.list</code></strong>中添加 Docker 软件源</p>
<pre><code>$ sudo add-apt-repository \
    &quot;deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
    $(lsb_release -cs) \
    stable&quot;


# 官方源
# $ sudo add-apt-repository \
#    &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu \
#    $(lsb_release -cs) \
#    stable&quot;
</code></pre><blockquote>
<p>以上命令会添加稳定版本的 Docker CE APT 镜像源，如果需要测试或每日构建版本的 Docker CE 请将 stable 改为 test 或者 nightly。</p>
</blockquote>
<h3 id="安装-docker-ce">安装 Docker CE</h3>
<p>更新 apt 软件包缓存，并安装<strong><code>docker-ce</code></strong>：</p>
<pre><code>$ sudo apt-get update

$ sudo apt-get install docker-ce
</code></pre><h3 id="使用脚本自动安装">使用脚本自动安装</h3>
<p>在测试或开发环境中 Docker 官方为了简化安装流程，提供了一套便捷的安装脚本，Ubuntu 系统上可以使用这套脚本安装：</p>
<pre><code>$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun
</code></pre><p>执行这个命令后，脚本就会自动的将一切准备工作做好，并且把 Docker CE 的 Edge 版本安装在系统中。</p>
<h2 id="centos7安装docker-ce">centos7安装Docker CE</h2>
<ul>
<li><a href="https://docs.docker.com/install/linux/docker-ce/centos/">官方文档</a><a href="https://docs.docker.com/install/linux/docker-ce/centos/">https://docs.docker.com/install/linux/docker-ce/centos/</a></li>
</ul>
<h3 id="系统要求-1">系统要求</h3>
<p>1、安装Docker CE，需要一个维护版本的Centos7</p>
<p>2、centos-extras库必须启用，这个存储库默认启用，但是如果您禁用了它，您需要重新启用它（<a href="https://wiki.centos.org/AdditionalResources/Repositories">https://wiki.centos.org/AdditionalResources/Repositories</a>）</p>
<h3 id="卸载旧版本-1">卸载旧版本</h3>
<pre><code>yum remove docker docker-common docker-selinux
</code></pre><h3 id="安装docker-ce">安装Docker CE</h3>
<ol>
<li>使用仓库安装</li>
</ol>
<ul>
<li>安装需要的依赖包</li>
</ul>
<pre><code>yum install -y yum-utils device-mapper-persistent-data
</code></pre><ul>
<li>配置稳定仓库</li>
</ul>
<pre><code>yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
</code></pre><ul>
<li>安装</li>
</ul>
<pre><code>yum install docker-ce
</code></pre><ul>
<li>安装指定版本的Docker</li>
</ul>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="p">[</span><span class="nx">root</span><span class="err">@</span><span class="nx">docker</span> <span class="err">~</span><span class="p">]</span><span class="err">#</span> <span class="nx">yum</span> <span class="nx">list</span> <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span> <span class="o">--</span><span class="nx">showduplicates</span> <span class="p">|</span> <span class="nx">sort</span> <span class="o">-</span><span class="nx">r</span>
 <span class="o">*</span> <span class="nx">updates</span><span class="p">:</span> <span class="nx">mirrors</span><span class="p">.</span><span class="nx">aliyun</span><span class="p">.</span><span class="nx">com</span>
<span class="nx">Loading</span> <span class="nx">mirror</span> <span class="nx">speeds</span> <span class="nx">from</span> <span class="nx">cached</span> <span class="nx">hostfile</span>
<span class="nx">Loaded</span> <span class="nx">plugins</span><span class="p">:</span> <span class="nx">fastestmirror</span>
<span class="nx">Installed</span> <span class="nx">Packages</span>
 <span class="o">*</span> <span class="nx">extras</span><span class="p">:</span> <span class="nx">mirrors</span><span class="p">.</span><span class="nx">aliyun</span><span class="p">.</span><span class="nx">com</span>
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.09.0</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.09.0</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="err">@</span><span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span>
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.06.2</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.06.1</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.06.0</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.03.2</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.03.1</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
<span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="p">.</span><span class="nx">x86_64</span>            <span class="mf">17.03.0</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span>            <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="nx">stable</span> 
 <span class="o">*</span> <span class="nx">base</span><span class="p">:</span> <span class="nx">mirrors</span><span class="p">.</span><span class="nx">aliyun</span><span class="p">.</span><span class="nx">com</span>
<span class="nx">Available</span> <span class="nx">Packages</span>

<span class="p">[</span><span class="nx">root</span><span class="err">@</span><span class="nx">docker</span> <span class="err">~</span><span class="p">]</span><span class="err">#</span> <span class="nx">yum</span> <span class="nx">install</span> <span class="nx">docker</span><span class="o">-</span><span class="nx">ce</span><span class="o">-</span><span class="mf">17.06.0</span><span class="p">.</span><span class="nx">ce</span><span class="o">-</span><span class="mf">1.</span><span class="nx">el7</span><span class="p">.</span><span class="nx">centos</span><span class="p">.</span><span class="nx">x86_64</span>
</code></pre></div><ol start="2">
<li>使用rpm包安装docker</li>
</ol>
<ul>
<li>
<p>稳定版下载地址：
<a href="https://download.docker.com/linux/centos/7/x86_64/stable/Packages/">https://download.docker.com/linux/centos/7/x86_64/stable/Packages/</a></p>
</li>
<li>
<p>安装</p>
</li>
</ul>
<pre><code>yum install -y /path/to/package.rpm
</code></pre><ul>
<li>启动</li>
</ul>
<pre><code>systemctl start docker
</code></pre><ul>
<li>版本升级</li>
</ul>
<pre><code>yum -y upgrade  /path/to/package.rpm
</code></pre><ol start="3">
<li>使用已有脚本安装</li>
</ol>
<pre><code>curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh
</code></pre><ol start="4">
<li>配置Docker开机自启动</li>
</ol>
<pre><code>systemctl enable docker
</code></pre><h2 id="启动-docker-ce">启动 Docker CE</h2>
<pre><code>$ sudo systemctl enable docker
$ sudo systemctl start docker
</code></pre><p>Ubuntu 14.04 请使用以下命令启动：</p>
<pre><code>$ sudo service docker start
</code></pre><h2 id="卸载docker">卸载Docker</h2>
<ul>
<li>卸载docker-ce</li>
</ul>
<pre><code>yum remove docker-ce
</code></pre><ul>
<li>在您的主机上的镜像、容器、卷或自定义配置文件不会自动删除，要删除所有图像、容器和卷:</li>
</ul>
<pre><code>rm -rf /var/lib/docker
</code></pre><h2 id="建立-docker-用户组">建立 docker 用户组</h2>
<p>默认情况下，<strong><code>docker</code></strong>命令会使用<a href="https://en.wikipedia.org/wiki/Unix_domain_socket"> Unix socket </a>与 Docker 引擎通讯。而只有<strong><code>root</code></strong>用户和<strong><code>docker</code></strong>组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑，一般 Linux 系统上不会直接使用<strong><code>root</code></strong>用户。因此，更好地做法是将需要使用<strong><code>docker</code></strong>的用户加入<strong><code>docker</code></strong>用户组。</p>
<p>建立<strong><code>docker</code></strong>组：</p>
<pre><code>$ sudo groupadd docker
</code></pre><p>将当前用户加入<strong><code>docker</code></strong>组：</p>
<pre><code>$ sudo usermod -aG docker $USER
</code></pre><p>退出当前终端并重新登录，进行如下测试。</p>
<h2 id="测试-docker-是否安装正确">测试 Docker 是否安装正确</h2>
<pre><code>$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
</code></pre><p>若能正常输出以上信息，则说明安装成功。</p>
<p>** 参考 ** <a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/">Docker 官方 Ubuntu 安装文档</a></p>
<h2 id="镜像加速">镜像加速</h2>
<p>如果在使用过程中发现拉取 Docker 镜像十分缓慢，可以配置 Docker 国内镜像加速。</p>
<p>国内从 Docker Hub 拉取镜像有时会遇到困难，此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务，例如：</p>
<ul>
<li><a href="https://docs.docker.com/registry/recipes/mirror/#use-case-the-china-registry-mirror">Docker 官方提供的中国 registry mirror <code>https://registry.docker-cn.com</code></a></li>
<li><a href="https://cr.console.aliyun.com/cn-hangzhou/mirrors">阿里云加速器(需登录账号获取)</a></li>
<li><a href="https://kirk-enterprise.github.io/hub-docs/#/user-guide/mirror">七牛云加速器 <code>https://reg-mirror.qiniu.com/</code></a></li>
</ul>
<blockquote>
<p>当配置某一个加速器地址之后，若发现拉取不到镜像，请切换到另一个加速器地址。</p>
<p>国内各大云服务商均提供了 Docker 镜像加速服务，建议根据运行 Docker 的云平台选择对应的镜像加速服务。</p>
</blockquote>
<p>我们以 Docker 官方加速器 <code>https://registry.docker-cn.com</code> 为例进行介绍。</p>
<h2 id="ubuntu-1404debian-7-wheezy">Ubuntu 14.04、Debian 7 Wheezy</h2>
<p>对于使用 <code>upstart</code> 的系统而言，编辑 <code>/etc/default/docker</code> 文件，在其中的 <code>DOCKER_OPTS</code> 中配置加速器地址：</p>
<pre><code>DOCKER_OPTS=&quot;--registry-mirror=https://registry.docker-cn.com&quot;
</code></pre><p>重新启动服务。</p>
<pre><code>$ sudo service docker restart
</code></pre><h2 id="ubuntu-1604debian-8centos-7">Ubuntu 16.04+、Debian 8+、CentOS 7</h2>
<p>对于使用 systemd 的系统，请在 <code>/etc/docker/daemon.json</code> 中写入如下内容（如果文件不存在请新建该文件）</p>
<pre><code>{
  &quot;registry-mirrors&quot;: [
    &quot;https://registry.docker-cn.com&quot;
  ]
}
</code></pre><blockquote>
<p>注意，一定要保证该文件符合 json 规范，否则 Docker 将不能启动。</p>
</blockquote>
<p>之后重新启动服务。</p>
<pre><code>$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
</code></pre><blockquote>
<p>注意：如果您之前查看旧教程，修改了 docker.service 文件内容，请去掉您添加的内容（&ndash;registry-mirror=https://registry.docker-cn.com），这里不再赘述。</p>
</blockquote>
<h2 id="windows-10">Windows 10</h2>
<p>对于使用 Windows 10 的系统，在系统右下角托盘 Docker 图标内右键菜单选择 <code>Settings</code>，打开配置窗口后左侧导航菜单选择 <code>Daemon</code>。在 <code>Registry mirrors</code> 一栏中填写加速器地址 <code>https://registry.docker-cn.com</code>，之后点击 <code>Apply</code> 保存后 Docker 就会重启并应用配置的镜像地址了。</p>
<h2 id="macos">macOS</h2>
<p>对于使用 macOS 的用户，在任务栏点击 Docker for mac 应用图标 -&gt; Perferences&hellip; -&gt; Daemon -&gt; Registry mirrors。在列表中填写加速器地址 <code>https://registry.docker-cn.com</code>。修改完成之后，点击 <code>Apply &amp; Restart</code> 按钮，Docker 就会重启并应用配置的镜像地址了。</p>
<h2 id="检查加速器是否生效">检查加速器是否生效</h2>
<p>命令行执行 <code>docker info</code>，如果从结果中看到了如下内容，说明配置成功。</p>
<pre><code>Registry Mirrors:
 https://registry.docker-cn.com/
</code></pre>
                
              </div>

              
                <span class="post-meta">
                  
                    #<a href="https://systemime.github.iotags/docker/">Docker</a>&nbsp;
                  
                    #<a href="https://systemime.github.iotags/%E5%AE%89%E8%A3%85/">安装</a>&nbsp;
                  
                </span>
              

            </article>
          
            <article class="post-preview">
              <a href="https://systemime.github.io/blog/2019-01/docker%E5%85%A5%E9%97%A8/">
                <h2 class="post-title">Docker入门</h2>

                
              </a>

              <p class="post-meta">
                Posted on January 2, 2019
              </p>
              <div class="post-entry">
                
                  <h1 id="docker简介">Docker简介</h1>
<p>Docker 是 Golang 编写的， 自 2013 年推出以来，受到越来越多的开发者的关注。如果你关注最新的技术发展，那么你一定听说过 Docker。不管是云服务还是微服务（Microservices），越来越多的厂商都开始基于 Docker 作为基础设施自动化的工具。那么什么是 Docker？Docker与传统的虚拟机有什么区别？为何要采用 Docker？如何使用 Docker？</p>
<p>本文，就针对上述提到的问题，来简单介绍下 Docker。</p>
<blockquote>
<p>本文所述<strong><code>安装方法</code></strong>适用<code>ubuntu14.04</code>及以前版本，<code>16.04</code>版本及以上请参阅：<a href="https://qfdxz.top/2019/02/12/Ubuntu-%E5%AE%89%E8%A3%85-Docker-CE/#more">Ubuntu 安装 Docker CE</a></p>
</blockquote>
<!-- raw HTML omitted -->
<p>Docker 可以让你将所有应用软件以及它的以来打包成软件开发的标准化单元。</p>
<p>Docker容器将软件以及它运行安装所需的一切文件（代码、运行时、系统工具、系统库）打包到一起，这就保证了不管是在什么样的运行环境，总是能以相同的方式运行。就好像 Java 虚拟机一样，“一次编写，到处运行（Write once, run anywhere）”，而 Docker 是“一次构建，到处运行（Build once，run anywhere）”。</p>
<p><img src="https://raw.githubusercontent.com/systemime/my_image/master/docker%E7%AE%80%E4%BB%8B/640.jpg" alt="图片1"></p>
<p>Docker 是一种“容器即服务”（Docker Containers as a Service ，简称 CaaS），使得开发和IT运营团队可以对于应用的构建、发布、运行更加敏捷和可控。</p>
<p>概括的说： <code>Docker</code> 是为开发人员和系统管理员用于构建、发布、并运行分布式应用程序的开放式平台。该平台由 <code>Docker</code> 引擎（一个便携、轻巧的运行时和打包工具） 和 <code>Docker Hub</code> （一个共享应用程序和自动化工作流的云服务）等组成。<code>Docker</code> 可以使应用程序从组件迅速组装并消除了开发、质量保证和生产环境之间的摩擦问题。这样一来，IT部门可以更快地发布，而这些应用程序不管是运行在笔记本电脑、数据中心的虚拟机，还是任何的云，其运行过程和结果都是一致的。</p>
<p>我们再来看下 Docker 的 Logo 。很明显，这是一只鲸鱼，它托着许多集装箱。我们可以把宿主机可当做这只鲸鱼，把相互隔离的容器可看成集装箱，每个集装箱中都包含自己的应用程序。这 Logo 简直的太形象了！</p>
<p><img src="https://raw.githubusercontent.com/systemime/my_image/master/docker%E7%AE%80%E4%BB%8B/docker.jpg" alt="logo"></p>
<h2 id="docker-的优点">Docker 的优点</h2>
<ul>
<li>轻量级：所有容器在一台机器上共享同一个操作系统内核，这样他们立即开始，并更有效地利用内存。Image 是从分层文件系统的构建，这样他们能够共享公共文件，使得磁盘使用率和 Image 的下载更加高效。</li>
<li>开放：Docker 容器是基于开发的标准，允许容器运行在主流的 Linux 发布版和 Microsoft 操作系统作为所有的基础设施。</li>
<li>安全：容器使得应用程序彼此隔离，而基础架构同时为应用程序提供了额外的保护层。</li>
</ul>
<h2 id="docker-与-虚拟机的区别">Docker 与 虚拟机的区别</h2>
<p>容器与虚拟机有着类似的资源隔离和分配的优点，但不同的架构方法使容器能够更加便携，高效等。</p>
<h3 id="虚拟机的架构">虚拟机的架构</h3>
<p><img src="https://raw.githubusercontent.com/systemime/my_image/master/docker%E7%AE%80%E4%BB%8B/%E6%9E%B6%E6%9E%84.jpg" alt="虚拟机架构"><br>
每个虚拟机都包括应用程序、必要的二进制文件和库以及一个完整的客户操作系统(Guest OS)，尽管它们被分离，它们共享并利用主机的硬件资源，将近需要十几个 GB 的大小。</p>
<h3 id="容器的架构">容器的架构</h3>
<p><img src="https://raw.githubusercontent.com/systemime/my_image/master/docker%E7%AE%80%E4%BB%8B/%E5%AE%B9%E5%99%A8%E6%9E%B6%E6%9E%84.jpg" alt="容器架构"></p>
<p>容器包括应用程序及其所有的依赖，但与其他容器共享内核。它们以独立的用户空间进程形式运行在主机操作系统上。他们也不依赖于任何特定的基础设施，Docker 容器可以运行在任何计算机上，任何基础设施和任何云上。</p>
<p>Docker 的容器利用了 LXC，管理利用了 namespaces 来做权限的控制和隔离，cgroups 来进行资源的配置，并且还通过 aufs 来进一步提高文件系统的资源利用率，而这些技术都不是 Docker 独创。</p>
<h3 id="lxc">LXC</h3>
<p>LXC 与虚拟机的不同之处在于，它是一个操作系统级别的虚拟化环境，而不是硬件虚拟化环境。他们都做同样的事情，但 LXC 是操作系统级别的虚拟化环境，虚拟环境有它自己的进程和网络空间，而不是创建一个完整成熟的虚拟机。因此，一个 LXC 虚拟操作系统具有最小的资源需求，并启动只需几秒钟。</p>
<p>正如你可以在下图中看到的，左侧是 LXC 虚拟的 Ubuntu ，默认安装使用 11 MB 大小。
<img src="https://raw.githubusercontent.com/systemime/my_image/master/docker%E7%AE%80%E4%BB%8B/lxc.jpg" alt="LXC"></p>
<h3 id="docker-与-microservices-的关系">Docker 与 Microservices 的关系</h3>
<p>Microservices（微服务） 依赖于“基础设施自动化”，而 Docker 正是“基础设施自动化”的利器。可以说 Docker 的火爆，一定程度上也带动了微服务架构的兴起，而微服务的广泛应用也促进了 Docker 繁荣。可以说两者相辅相成。</p>
<p>有关微服务的介绍，可以移步至《简述 Microservices（微服务）》。</p>
<blockquote>
<p><a href="http://www.importnew.com/24651.html">http://www.importnew.com/24651.html</a></p>
</blockquote>
<h2 id="为什么要用-docker">为什么要用 Docker</h2>
<ul>
<li>开发更加敏捷：Docker 让开发人员可以自由定义环境，创建和部署的应用程序更快、更容易，IT 运维人员快速应对变化也更加灵活性。</li>
<li>更加可控：Docker 使得开发人员保存从基础设施到应用的代码，帮助 IT 运维人管理拥有标准的、安全的、可扩展的操作环境。</li>
<li>高可移植性：Docker 允许自由选择，可以是从笔记本电脑到一个团队，从私人基础设施到公共云提供商。</li>
</ul>
<p>这样，你可以专注于开发应用，其他的繁琐事交给 Docker 去做吧。</p>
<h2 id="如何使用-docker">如何使用 Docker</h2>
<p>这可真是一个大话题，如果完整阐述足够写一本书了。好在我们的目的是做入门普及，因此我们就简单讲一下 Docker 的安装、基本使用和常用命令。</p>
<h3 id="docker安装">Docker安装</h3>
<blockquote>
<p>注意！！！本文下面所记录的docker命令或安装方式部分已经过时，请参照较新的博文进行参考
注意！！！本文下面所记录的docker命令或安装方式部分已经过时，请参照较新的博文进行参考
注意！！！本文下面所记录的docker命令或安装方式部分已经过时，请参照较新的博文进行参考</p>
<p>请参阅本站《Docker操作命令》与《Ubuntu安装Docker-CE》，在本站可以直接使用 搜索功能</p>
</blockquote>
<p>建议在linux环境下安装Docker，window环境搭建比较复杂且容易出错，使用Centos7+yum来安装Docker环境很方便。</p>
<p>Docker 软件包已经包括在默认的 CentOS-Extras 软件源里。因此想要安装 docker，只需要运行下面的 yum 命令：</p>
<blockquote>
<p>yum install docker</p>
</blockquote>
<p>安装完成后，使用下面的命令来启动 docker 服务，并将其设置为开机启动：</p>
<blockquote>
<p>service docker start<!-- raw HTML omitted -->
chkconfig docker on</p>
</blockquote>
<p>LCTT 译注：此处采用了旧式的 sysv 语法，如采用CentOS 7中支持的新式 systemd 语法，如下：</p>
<blockquote>
<p>systemctl docker start<!-- raw HTML omitted -->
chkconfig docker on</p>
</blockquote>
<h3 id="测试">测试</h3>
<blockquote>
<p>docker version</p>
</blockquote>
<p>输入上述命令，返回docker的版本相关信息，证明docker安装成功。</p>
<h2 id="hello-world">Hello World</h2>
<p>下面，我们通过最简单的 image 文件&quot;hello world&rdquo;，感受一下 Docker。</p>
<p>因为国内连接 Docker 的官方仓库很慢，因此我们在日常使用中会使用Docker 中国加速器。通过 Docker 官方镜像加速，中国区用户能够快速访问最流行的 Docker 镜像。该镜像托管于中国大陆，本地用户现在将会享受到更快的下载速度和更强的稳定性，从而能够更敏捷地开发和交付 Docker 化应用。</p>
<p>Docker 中国官方镜像加速可通过 registry.docker-cn.com访问。该镜像库只包含流行的公有镜像，私有镜像仍需要从美国镜像库中拉取。</p>
<p>修改系统中docker对应的配置文件即可，如下：</p>
<pre><code>vi /etc/docker/daemon.json
# 添加后
&quot;registry-mirrors&quot;: [&quot;https://registry.docker-cn.com&quot;],
&quot;live-restore&quot;: true
}
</code></pre><p>运行下面的命令，将 image 文件从仓库抓取到本地。</p>
<pre><code>docker pull library/hello-world
</code></pre><p>上面代码中，docker image pull是抓取 image 文件的命令。library/hello-world是 image 文件在仓库里面的位置，其中library是 image 文件所在的组，hello-world是 image 文件的名字。</p>
<p>抓取成功以后，就可以在本机看到这个 image 文件了。</p>
<pre><code>&gt; docker images 
&gt; 显示结果 
&gt; REPOSITORY TAG IMAGE ID CREATED SIZE&lt;br&gt;
&gt; docker.io/hello-world latest f2a91732366c 3 months ago 1.848 kB
</code></pre><p>现在，运行这个 image 文件。</p>
<pre><code>&gt; docker run hello-world
&gt; # 显示结果 
&gt; Hello from Docker! 
&gt; This message shows that your installation appears to be working correctly. 
&gt; ... 
</code></pre><p>输出这段提示以后，hello world就会停止运行，容器自动终止。有些容器不会自动终止，因为提供的是服务，比如Mysql镜像等。</p>
<h2 id="常用命令">常用命令</h2>
<p>除过以上我们使用的Docker命令外，Docker还有一些其它常用的命令。</p>
<p>拉取docker镜像</p>
<pre><code>docker pull image_name
</code></pre><p>查看宿主机上的镜像，Docker镜像保存在/var/lib/docker目录下:</p>
<pre><code>docker images
</code></pre><p>删除镜像</p>
<pre><code>docker rmi docker.io/tomcat:7.0.77-jre7 或者 docker rmi b39c68b7af30
</code></pre><p>查看当前有哪些容器正在运行</p>
<pre><code>docker ps
</code></pre><p>查看所有容器</p>
<pre><code>docker ps -a
</code></pre><p>启动、停止、重启容器命令：</p>
<pre><code>&gt;docker start container_name/container_id&lt;br&gt;
&gt;docker stop container_name/container_id&lt;br&gt;
&gt;docker restart container_name/container_id
</code></pre><p>后台启动一个容器后，如果想进入到这个容器，可以使用attach命令：</p>
<pre><code>docker attach container_name/container_id
</code></pre><pre><code>删除容器的命令
&gt;docker rm container_name/container_id

查看当前系统Docker信息
&gt;docker info

从Docker hub上下载某个镜像:
&gt;docker pull centos:latest&lt;br&gt;
&gt;docker pull centos:latest
</code></pre><p>执行docker pull centos会将Centos这个仓库下面的所有镜像下载到本地repository。</p>

                
              </div>

              
                <span class="post-meta">
                  
                    #<a href="https://systemime.github.iotags/docker/">Docker</a>&nbsp;
                  
                    #<a href="https://systemime.github.iotags/%E5%9F%BA%E7%A1%80/">基础</a>&nbsp;
                  
                </span>
              

            </article>
          
        </div>
        
          <ul class="pager main-pager">
            
              <li class="previous">
                <a href="https://systemime.github.io/categories/docker/page/3">&larr; Newer Posts</a>
              </li>
            
            
          </ul>
        
      </div>
    <div align="center" class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
        

<ul class="pagination">
    
    <li class="page-item">
        <a href="https://systemime.github.io/categories/docker/" class="page-link" aria-label="First"><span aria-hidden="true">&laquo;&laquo;</span></a>
    </li>
    
    <li class="page-item">
    <a href="https://systemime.github.io/categories/docker/page/3/" class="page-link" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a>
    </li>
    
    
    
    
    
    
    
        
        
    
    
    <li class="page-item"><a class="page-link" href="https://systemime.github.io/categories/docker/">1</a></li>
    
    
    
    
    
    
        
        
    
    
    <li class="page-item"><a class="page-link" href="https://systemime.github.io/categories/docker/page/2/">2</a></li>
    
    
    
    
    
    
        
        
    
    
    <li class="page-item"><a class="page-link" href="https://systemime.github.io/categories/docker/page/3/">3</a></li>
    
    
    
    
    
    
        
        
    
    
    <li class="page-item active"><a class="page-link" href="https://systemime.github.io/categories/docker/page/4/">4</a></li>
    
    
    <li class="page-item disabled">
    <a  class="page-link" aria-label="Next"><span aria-hidden="true">&raquo;</span></a>
    </li>
    
    <li class="page-item">
        <a href="https://systemime.github.io/categories/docker/page/4/" class="page-link" aria-label="Last"><span aria-hidden="true">&raquo;&raquo;</span></a>
    </li>
    
</ul>


        
        
        
        
      </div>
    </div>
  </div>

    <div class="rocket"><img src="https://systemime.github.io/img/rocket_up.png" alt="" width="100" height="100"></div>
<script>
    $(function () {
      $(window).scroll(function () {
        
        if ($(window).scrollTop() >= 1000) {
          $('.rocket').stop().fadeIn(1000);
        }else {
          $('.rocket').stop().fadeOut(1000);
        }
      })
      
      $('.rocket').click(function () {
        $('html,body').stop().animate({scrollTop:0},400);
       
      })
    })

</script>

    
    <div id="landlord">
        <div class="message" style="opacity:0"></div>
        <canvas id="live2d" width="280" height="250" class="live2d"></canvas>
        <div class="hide-button">隐藏</div>
    </div>
    
    <footer id="copyright">
  <div class="container">
    <div class="row">
      <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
        <ul class="list-inline text-center footer-links">
          
          
        </ul>
        <p class="credits copyright text-muted">
        &copy;2017-2018
          
          &nbsp;&bull;&nbsp;
          September 20,2019
          updated
          
            &nbsp;&bull;&nbsp;
            <a href="https://systemime.github.io">Home</a>
          
        </p>

        <p class="credits theme-by text-muted">
        <a href="https://servicemesher.github.io" target="_blank">ServiceMesher</a>&nbsp;&bull;&nbsp;<a href="https://github.com/yangchuansheng/k8s-knowledge" target="_blank">Kubernetes 知识图谱</a>
        <br />
        <span id="busuanzi_container_site_pv">
            本站已被访问 <span id="busuanzi_value_site_pv"></span> 次啦
        </span>
        <span id="busuanzi_container_site_uv">
            &ensp;|&ensp;&thinsp;您是第 <span id="busuanzi_value_site_uv"></span> 位到访的小伙伴喔
        </span>
        </p>
        
        <p class="credits theme-by text-muted">
          <a href="http://gohugo.io">Hugo v0.67.0</a> powered &nbsp;&bull;&nbsp; Theme by <a href="http://deanattali.com/beautiful-jekyll/">Beautiful Jekyll</a> adapted to <a href="https://github.com/halogenica/beautifulhugo">Beautiful Hugo</a>
          
          <center><section class="credits theme-by text-muted">
    <span class="footer__copyright">
    <div>
    <span id="span_dt_dt"> </span>
    <script language="javascript">
      function show_date_time(){
        window.setTimeout("show_date_time()", 1000);
        BirthDay=new Date("7/8/2017 10:56:12");
        today=new Date();
        timeold=(today.getTime()-BirthDay.getTime());
        sectimeold=timeold/1000
        secondsold=Math.floor(sectimeold);
        msPerDay=24*60*60*1000
        e_daysold=timeold/msPerDay
        daysold=Math.floor(e_daysold);
        e_hrsold=(e_daysold-daysold)*24;
        hrsold=Math.floor(e_hrsold);
        e_minsold=(e_hrsold-hrsold)*60;
        minsold=Math.floor((e_hrsold-hrsold)*60);
        seconds=Math.floor((e_minsold-minsold)*60);
        span_dt_dt.innerHTML="本博客已经开心运行 "+daysold+" 天 "+hrsold+" 小时 "+minsold+" 分 "+seconds+" 秒";
      }
      show_date_time();
    </script>
    </div>
</script>
</section>
</center>
        </p>
      </div>
    </div>
  </div>
</footer>


<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/bootstrap.min.js"></script>
<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/photoswipe-ui-default.min.js"></script>
<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/photoswipe.min.js"></script>
<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/auto-render.min.js"></script>
<script src="https://systemime.github.io/js/main.js"></script>
<script src="https://systemime.github.io/js/clipboard.min.js"></script>
<script src="https://systemime.github.io/js/prism.js?t=123"></script>
<script src="https://systemime.github.io/js/highlight.min.js"></script>
<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/katex.min.js"></script>
<script src="https://systemime.github.io/js/reward.js"></script>
<script src="https://systemime.github.io/js/canvas_ribbon.js"></script>
<script> renderMathInElement(document.body); </script>

<script src="https://systemime.github.io/js/baguetteBox.js"></script>
<script> baguetteBox.run('.gallery');</script>






<script async defer src="https://buttons.github.io/buttons.js"></script>


<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
<script type="text/javascript" src="https://systemime.github.io/js/quicklink.umd.js"></script>
<script>
window.addEventListener('load', () =>{
   quicklink();
});
</script>



<script type="text/javascript">
 
var a_idx = 0;
jQuery(document).ready(function($) {
    $("body").click(function(e) {
        var a = new Array("Docker", "Kubernetes", "Prometheus", "Envoy", "Istio", "Service Mesh", "Cloud Native");
        var $i = $("<span />").text(a[a_idx]);
        a_idx = (a_idx + 1) % a.length;
        var x = e.pageX,
        y = e.pageY;
        function randomColor() {
          var flakeColor = new Array("#FFDA65", "#00BFFF", "#BA55D3", "#FFA07A", "#87CEEB", "#FFB6C1");
          var snowColor = flakeColor[Math.floor(flakeColor.length * Math.random())];
          return snowColor;
        }
        $i.css({
            "z-index": 999999999999999999999999999999999999999999999999999999999999999999999,
            "top": y - 20,
            "left": x,
            "position": "absolute",
            "font-weight": "bold",
            "color": randomColor()
        });
        $("body").append($i);
        $i.animate({
            "top": y - 180,
            "opacity": 0
        },
        1500,
        function() {
            $i.remove();
        });
    });
});
</script>


<script type="text/javascript" src="https://systemime.github.io/js/lightbox.js"></script>











<script src="https://cdn.plyr.io/3.4.7/plyr.js"></script>
<script>const player = new Plyr('#player');</script>


<script async defer src="https://buttons.github.io/buttons.js"></script>


  </body>
</html>

